﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Mvc;
using System.Web.UI;
using PortalCurriculo.Domain;
using PortalCurriculo.Domain.Utils;
using PortalCurriculo.MvcWebUI.Models;

namespace PortalCurriculo.MvcWebUI.Controllers
{
    public class ControllerBase : Helpers.Session
    {
        public Pessoa PessoaLogada
        {
            set { WebSession(SessionName.PessoaLogada, value); }
            get
            {
                var pessoaLogada = WebSession<Pessoa>(SessionName.PessoaLogada);

                if (pessoaLogada == null)
                    new AccountController { ControllerContext = ControllerContext }.LogOff();

                return pessoaLogada;
            }
        }
        public PessoaModel PessoaLogadaModel
        {
            get { return new PessoaModel().CriarViewModel(PessoaLogada); }
        }
    }


    public class ControllerBase<T, TEntity> : ControllerBase
        where T : ModelBase<T, TEntity>, new()
        where TEntity : GeneralAction, new()
    {

        #region Propriedades

        private GeneralAction dominio;

        private GeneralAction Dominio
        {
            get { return dominio ?? (dominio = (GeneralAction)Activator.CreateInstance(typeof(TEntity))); }
        }

        //public int IdUsuarioLogado
        //{
        //    get { return PessoaLogada.IdUsuario; }
        //}

        public bool UsuarioLogado
        {
            get { return PessoaLogada != null; }
        }

        public IEnumerable<GeneralAction> Dominios { get; set; }
        public T Model { get; set; }
        public List<T> Models { get; set; }

        #endregion

        #region Actions

        #region Manter

        public virtual ActionResult Manter(int? id)
        {
            if (id == null)
                return Criar();

            var method = GetType().GetMethod("CarregarEdicao");
            var model = method.Invoke(this, new object[] { (int)id, true });

            return View(model);
        }

        [HttpPost]
        public virtual ActionResult Manter(T model)
        {
            try
            {
                if (!Validar(model))
                {
                    ExecutarMetodoPreencherViewBag(model);
                    return View();
                }

                model.Ativo = true;

                if (model.Id == 0)
                {
                    var dominioCriar = model.CriarDominio();
                    dominioCriar.Save(dominioCriar);
                    Model = model.CriarViewModel(dominioCriar);
                }
                else
                {
                    SalvarEdicao(model);
                    Model = model;
                }

                return RedirectToAction("Index");
            }
            catch
            {
                ExecutarMetodoPreencherViewBag(model);
                return View();
            }
        }

        #endregion

        #region Criar

        public virtual ActionResult Criar()
        {
            ExecutarMetodoPreencherViewBag(new T());

            return View();
        }

        [HttpPost]
        public virtual ActionResult Criar(T model)
        {
            return Manter(model);
        }

        #endregion

        #region Index

        public virtual ActionResult Index()
        {
            var method = ObterMetodoGenerico("SelectAllActive");

            var dominios = Dominios = (IEnumerable<GeneralAction>)method.Invoke(Dominio, null);
            var instanciaModel = (T)Activator.CreateInstance(typeof(T));
            Models = (List<T>)instanciaModel.CriarViewModel(dominios);
            return View(Models);
        }

        #endregion

        #region Detalhes

        public virtual ActionResult Detalhes(int id)
        {
            var method = ObterMetodoGenerico("SelectById");

            var dominioBanco = (GeneralAction)method.Invoke(Dominio, new object[] { id });
            var instanciaModel = (T)Activator.CreateInstance(typeof(T));
            Model = instanciaModel.CriarViewModel(dominioBanco);

            return View(Model);
        }

        #endregion

        #endregion

        public string RenderPartialViewToString(Controller controller, string viewName, object model)
        {
            var property = controller.GetType().GetMethod(viewName, new []{ typeof(int?) });
            var teste = (PartialViewResult) property.Invoke(controller, new object[] { 1 });
            teste.ViewName = "Manter";

            var sw = new StringWriter();
            ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
            var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);

            //teste.ExecuteResult(controller.ControllerContext);

            
            return null;
            //controller.ViewData.Model = model;
            //try
            //{
            //    using (var sw = new StringWriter())
            //    {
            //        ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
            //        var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
            //        viewResult.View.Render(viewContext, sw);

            //        return sw.GetStringBuilder().ToString();
            //    }
            //}
            //catch (Exception ex)
            //{
            //    return ex.ToString();
            //}
        }


        public string GetControlData(string controlPath, object model)
        {
            var control = new RazorView(ControllerContext, controlPath, null, false, null);
            ViewData.Model = model;

            var writer = new HtmlTextWriter(new StringWriter());
            control.Render(new ViewContext(ControllerContext, control, ViewData, TempData, writer), writer);
            string value = writer.InnerWriter.ToString();

            return value;
        }

        private MethodInfo ObterMetodoGenerico(string nomeMetodo)
        {
            var method = Dominio.GetType().GetMethod(nomeMetodo);
            return method.MakeGenericMethod(Dominio.GetType());
        }

        private void ExecutarMetodoPreencherViewBag(T model)
        {
            var metodo = GetType().GetMethod("PreencherViewBag") ?? GetType().GetMethod("PreencherViewBags");

            if (metodo == null) return;

            var parametros = metodo.GetParameters();
            var valoresParametros = new List<object>();

            foreach (var parametro in parametros)
            {
                var propriedade = typeof(T).GetProperty(string.Concat(parametro.Name.Substring(0, 1).ToUpper(),
                                                         parametro.Name.Substring(1, parametro.Name.Length - 1)));

                if (propriedade == null)
                {
                    valoresParametros.Add(null);
                    continue;
                }

                valoresParametros.Add(propriedade.GetValue(model, null));
            }

            metodo.Invoke(this, valoresParametros.ToArray());
        }

        #region Validação

        public void CorrigirMensagemErro()
        {
            var errosVs = ModelState.Values.Where(e => e.Errors.Count > 0 && e.Errors[0].ErrorMessage.StartsWith("The value '"));

            foreach (var erro in errosVs)
            {
                var mensagem = erro.Errors[0].ErrorMessage.Replace("The value", "O valor");
                mensagem = mensagem.Replace("is not valid for", "não é válido para").Replace("is not valid", "não é válido");

                erro.Errors[0] = new ModelError(mensagem);
            }
        }

        public bool Validar(ModelBase<T, TEntity> model)
        {
            CorrigirMensagemErro();

            //return ModelState.IsValid && ValidarModel(model);
            return ValidarModel(model);
        }

        public bool ValidarModel(ModelBase<T, TEntity> model)
        {
            var method = model.GetType().GetMethod("Validar");

            if (method == null) return true;

            var validacao = (Dictionary<string, string>)method.Invoke(model, new[] { model });

            if (validacao != null && validacao.Count > 0)
            {
                foreach (var keyPair in validacao)
                    ModelState.AddModelError(keyPair.Key, keyPair.Value);

                return false;
            }

            return true;
        }

        #endregion

        #region Edição

        public T SalvarEdicao(ModelBase<T, TEntity> model, bool usarIdSessao = false)
        {
            var nomeSessao = string.Concat(model.GetType().Name.Replace("Model", string.Empty), usarIdSessao ? model.Id.ToString() : string.Empty);

            var dominio =
                (TEntity)Session[nomeSessao] ??
                ((GeneralAction)Activator.CreateInstance(typeof(TEntity))).SelectById<TEntity>(model.Id);

            model.Ativo = true;
            dominio = model.CriarDominio(dominio);
            dominio.Save(dominio);

            Session[nomeSessao] = null;

            return model.CriarViewModel(dominio);
        }

        public T CarregarEdicao(int id, bool usarIdSessao = false)
        {
            var dominio = (TEntity)Activator.CreateInstance(typeof(TEntity));
            dominio = dominio.SelectById<TEntity>(id);
            Session[string.Concat(dominio.GetType().Name, usarIdSessao ? id.ToString() : string.Empty)] = dominio;

            var model = (ModelBase<T, TEntity>)Activator.CreateInstance(typeof(T));

            model = Model = model.CriarViewModel(dominio);

            ExecutarMetodoPreencherViewBag((T)model);

            return (T)model;
        }

        #endregion
    }

}
